﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace consoleTesting
{
    class GapsTestFibo
    {
        List<GapFibo> notClosedGaps = new List<GapFibo>();
        List<GapFibo> allGaps = new List<GapFibo>();
        List<GapFibo> closedGapsToClear = new List<GapFibo>();
        int point = 10000;
        decimal oneWeek = 6;

        public GapsTestFibo(List<Candle> candles)
        {
            GapFibo curGap = new GapFibo();
            Candle c = new Candle();
            bool flagForGap = false;
            decimal pullback = 0;
            decimal inGapMove = 0;

            for (int i = 0; i < candles.Count; i++)
            {
                c = candles[i];

                if ((c.Time.DayOfWeek == DayOfWeek.Sunday && !flagForGap)
                        || (c.Time.DayOfWeek == DayOfWeek.Monday && !flagForGap))
                {
                    flagForGap = true;

                    curGap = new GapFibo();

                    curGap.GapDate = c.Time;
                    curGap.GapCandleNumber = i;
                    curGap.PriceBeforeOpen = candles[i - 1].Close;
                    curGap.PriceAfterOpen = c.Open;
                    curGap.GapDurationBeforeCloseInDays = 99999999;//to protect against not closed gaps

                    if ((curGap.PriceAfterOpen - curGap.PriceBeforeOpen) > 0)
                        curGap.GapDirection = Direction.UP;

                    if ((curGap.PriceAfterOpen - curGap.PriceBeforeOpen) < 0)
                        curGap.GapDirection = Direction.DOWN;

                    if ((curGap.PriceAfterOpen - curGap.PriceBeforeOpen) == 0)
                        curGap.GapDirection = Direction.DODGE;


                    curGap.GapWidth = Math.Round((Math.Abs(curGap.PriceAfterOpen - curGap.PriceBeforeOpen) * point), 2);

                    curGap.PullbackPoints = 0;

                    if (curGap.GapDirection != Direction.DODGE)
                    {
                        notClosedGaps.Add(curGap);
                        allGaps.Add(curGap);
                    }


                }

                if (c.Time.DayOfWeek == DayOfWeek.Tuesday)
                    flagForGap = false;

                for (int j = 0; j < notClosedGaps.Count; j++)
                {
                    if (notClosedGaps[j].GapDate == new DateTime(2003, 09, 21))
                    {

                    }

                    if (notClosedGaps[j].GapDirection == Direction.UP)
                    {

                        pullback = Math.Abs(c.High - notClosedGaps[j].PriceAfterOpen) * point;
                        inGapMove = Math.Abs(c.Low - notClosedGaps[j].PriceAfterOpen) * point;

                        if (pullback > notClosedGaps[j].PullbackPoints)
                            notClosedGaps[j].PullbackPoints = pullback;

                        //if (inGapMove > notClosedGaps[j].InGapMove)
                        //    notClosedGaps[j].InGapMove = inGapMove;

                        if (c.Low <= notClosedGaps[j].PriceBeforeOpen)
                            AddGapAsClosed(c, notClosedGaps[j], i);
                    }

                    if (notClosedGaps[j].GapDirection == Direction.DOWN)
                    {
                        pullback = Math.Abs(c.Low - notClosedGaps[j].PriceAfterOpen) * point;
                        inGapMove = Math.Abs(c.High - notClosedGaps[j].PriceAfterOpen) * point;

                        if (pullback > notClosedGaps[j].PullbackPoints)
                            notClosedGaps[j].PullbackPoints = pullback;

                        //if (inGapMove > notClosedGaps[j].InGapMove)
                        //    notClosedGaps[j].InGapMove = inGapMove;

                        if (c.High >= notClosedGaps[j].PriceBeforeOpen)
                            AddGapAsClosed(c, notClosedGaps[j], i);
                    }
                }

                ClearClosedGaps();
            }

            string fileName = String.Format("Gaps_Fibo_{0}-{1}.xml", candles[0].Time.ToShortDateString().Replace("/", ""), candles[candles.Count - 1].Time.ToShortDateString().Replace("/", ""));
            Stream stream = File.Open(fileName, FileMode.Create);

            System.Xml.Serialization.XmlSerializer xmlSerGaps = new System.Xml.Serialization.XmlSerializer(allGaps.GetType());
            xmlSerGaps.Serialize(stream, allGaps);

            stream.Close();

            decimal sortPoint = 20;
            
            string sortExp = String.Format("Gap_Width_More_{0}_points", sortPoint);
            List<GapFibo> sortedList = allGaps.FindAll(x => x.GapWidth > sortPoint);
            String fileStats = String.Format("Gaps_Fibo_{0}-{1}_Stats_{2}.txt", candles[0].Time.ToShortDateString().Replace("/", ""), candles[candles.Count - 1].Time.ToShortDateString().Replace("/", ""), sortExp);
            StreamWriter sw = File.CreateText(fileStats);

            sw.WriteLine("Ширина гэпа > {0} пунктов", sortPoint);
            sw.WriteLine(String.Format("Средняя ширина гэпа: {0} пунктов", Math.Round(sortedList.Average(x => x.GapWidth), 2).ToString()));
            sw.WriteLine(String.Format("Максимальная ширина гэпа: {0} пунктов", Math.Round(sortedList.Max(x => x.GapWidth), 2).ToString()));
            sw.WriteLine(String.Format("Минимальная ширина гэпа: {0} пунктов", Math.Round(sortedList.Min(x => x.GapWidth), 2).ToString()));
            sw.WriteLine();

            sw.WriteLine("Гэпы закрывшиеся на той же неделе:");
            int closePullbackLess200 = sortedList.FindAll(x => x.PullbackPoints <200).Count;
            decimal percent0WeeksClose = (decimal)closePullbackLess200 / sortedList.Count * 100;
            sw.WriteLine(String.Format("Количество: {0}", closePullbackLess200));
            sw.WriteLine(String.Format("% от всех гэпов: {0} %", Math.Round(percent0WeeksClose, 2).ToString()));
            sw.WriteLine(String.Format("Общее количество пунктов гэпа: {0} пунктов", Math.Round(sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek).Sum(y => y.GapWidth), 2).ToString()));
            sw.WriteLine(String.Format("Максимальный откат от цены открытия до момента закрытия гэпа (гэп текущей недели): {0} пунктов", Math.Round(sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek).Max(y => y.PullbackPoints), 2).ToString()));
            
            sw.WriteLine();
            int less50 = sortedList.FindAll(x => x.PullbackPoints < 50).Count;
            int c50_100 = sortedList.FindAll(x =>  x.PullbackPoints >= 50 && x.PullbackPoints < 100).Count;
            int c100_200 = sortedList.FindAll(x =>  x.PullbackPoints >= 100 && x.PullbackPoints < 200).Count;
            //int c100_125 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 100 && x.PullbackPoints < 125).Count;
            //int c125_150 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 125 && x.PullbackPoints < 150).Count;
            //int c150_175 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 150 && x.PullbackPoints < 175).Count;
            //int c175_200 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 175 && x.PullbackPoints < 200).Count;
            int c200_300 = sortedList.FindAll(x =>  x.PullbackPoints >= 200 && x.PullbackPoints < 300).Count;
            int c300_400 = sortedList.FindAll(x =>  x.PullbackPoints >= 300 && x.PullbackPoints < 400).Count;
            int c400_500 = sortedList.FindAll(x =>  x.PullbackPoints >= 400 && x.PullbackPoints < 500).Count;
            int c500_600 = sortedList.FindAll(x =>  x.PullbackPoints >= 500 && x.PullbackPoints < 600).Count;
            int c600_700 = sortedList.FindAll(x =>  x.PullbackPoints >= 600 && x.PullbackPoints < 700).Count;
            int c700_800 = sortedList.FindAll(x =>  x.PullbackPoints >= 700 && x.PullbackPoints < 800).Count;
            int c800_900 = sortedList.FindAll(x =>  x.PullbackPoints >= 800 && x.PullbackPoints < 900).Count;
            int c900_1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 900 && x.PullbackPoints < 1000).Count;
            int cMore1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 1000).Count;

            sw.WriteLine(String.Format("% гэпов с откатом до 50 пунктов: {0} %", Math.Round((decimal)less50 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 100 пунктов: {0} %", Math.Round((decimal)c50_100 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 200 пунктов: {0} %", Math.Round((decimal)c100_200 / closePullbackLess200 * 100, 2)));
            //sw.WriteLine(String.Format("% гэпов с откатом до 125 пунктов: {0} %", Math.Round((decimal)c100_125 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% гэпов с откатом до 150 пунктов: {0} %", Math.Round((decimal)c125_150 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% гэпов с откатом до 175 пунктов: {0} %", Math.Round((decimal)c150_175 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% гэпов с откатом до 200 пунктов: {0} %", Math.Round((decimal)c175_200 / close0WeekCount * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 300 пунктов: {0} %", Math.Round((decimal)c200_300 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 400 пунктов: {0} %", Math.Round((decimal)c300_400 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 500 пунктов: {0} %", Math.Round((decimal)c400_500 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 600 пунктов: {0} %", Math.Round((decimal)c500_600 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 700 пунктов: {0} %", Math.Round((decimal)c600_700 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 800 пунктов: {0} %", Math.Round((decimal)c700_800 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 900 пунктов: {0} %", Math.Round((decimal)c800_900 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом до 1000 пунктов: {0} %", Math.Round((decimal)c900_1000 / closePullbackLess200 * 100, 2)));
            sw.WriteLine(String.Format("% гэпов с откатом более 1000 пунктов: {0} %", Math.Round((decimal)cMore1000 / closePullbackLess200 * 100, 2)));
            sw.WriteLine();
            sw.WriteLine(String.Format("Сумма пунктов гэпов с откатом до 100 пунктов: {0} пунктов", Math.Round(sortedList.FindAll(x => x.PullbackPoints < 200 && x.GapDurationBeforeCloseInDays == 0).Sum(y => y.GapWidth), 2)));
            int maxLossCount = 0;
            int maxProfitCount = 0;
            double avgLossCount = 0;
            double avgProfitCount = 0;
            ConsecuteviLossAndProfit(sortedList, 200, out maxLossCount, out maxProfitCount, out avgLossCount, out avgProfitCount);
            sw.WriteLine(String.Format("Фильтр {0} пунктов - максимальная серия убытков: {1}, максимальная серия прибыли: {2}, средняя убыточная серия: {3}, средняя прибыльная серия: {4}", 200, maxLossCount, maxProfitCount, avgLossCount, avgProfitCount));
            sw.WriteLine();

            foreach (int year in sortedList.Select(x => x.GapDate.Year).Distinct())
                WritePullacksByYear(sw, sortedList, year);

            sw.WriteLine("Всего гэпов найдено: {0}", sortedList.Count);
            sw.WriteLine(String.Format("Лет посчитано: {0}", (candles[candles.Count - 1].Time.Year - candles[0].Time.Year).ToString()));
            sw.Close();

        }

        private void WritePullacksByYear(StreamWriter sw, List<GapFibo> sortedList, int year)
        {
            int less50 = sortedList.FindAll(x => x.PullbackPoints < 50 && x.GapDate.Year == year).Count;
            int c50_100 = sortedList.FindAll(x => x.PullbackPoints >= 50 && x.PullbackPoints < 100 && x.GapDate.Year == year).Count;
            int c100_200 = sortedList.FindAll(x => x.PullbackPoints >= 100 && x.PullbackPoints < 200 && x.GapDate.Year == year).Count;
            int c100_125 = sortedList.FindAll(x =>  x.PullbackPoints >= 100 && x.PullbackPoints < 125 && x.GapDate.Year == year).Count;
            int c125_150 = sortedList.FindAll(x =>  x.PullbackPoints >= 125 && x.PullbackPoints < 150 && x.GapDate.Year == year).Count;
            int c150_175 = sortedList.FindAll(x =>  x.PullbackPoints >= 150 && x.PullbackPoints < 175 && x.GapDate.Year == year).Count;
            int c175_200 = sortedList.FindAll(x =>  x.PullbackPoints >= 175 && x.PullbackPoints < 200 && x.GapDate.Year == year).Count;
            int c200_300 = sortedList.FindAll(x =>  x.PullbackPoints >= 200 && x.PullbackPoints < 300 && x.GapDate.Year == year).Count;
            int c300_400 = sortedList.FindAll(x =>  x.PullbackPoints >= 300 && x.PullbackPoints < 400 && x.GapDate.Year == year).Count;
            int c400_500 = sortedList.FindAll(x =>  x.PullbackPoints >= 400 && x.PullbackPoints < 500 && x.GapDate.Year == year).Count;
            int c500_600 = sortedList.FindAll(x =>  x.PullbackPoints >= 500 && x.PullbackPoints < 600 && x.GapDate.Year == year).Count;
            int c600_700 = sortedList.FindAll(x =>  x.PullbackPoints >= 600 && x.PullbackPoints < 700 && x.GapDate.Year == year).Count;
            int c700_800 = sortedList.FindAll(x =>  x.PullbackPoints >= 700 && x.PullbackPoints < 800 && x.GapDate.Year == year).Count;
            int c800_900 = sortedList.FindAll(x =>  x.PullbackPoints >= 800 && x.PullbackPoints < 900 && x.GapDate.Year == year).Count;
            int c900_1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 900 && x.PullbackPoints < 1000 && x.GapDate.Year == year).Count;
            int cMore1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 1000 && x.GapDate.Year == year).Count;

            sw.WriteLine();
            sw.WriteLine("Общее кол-во сортированных гэпов в {0} году: {1} шт", year, sortedList.FindAll(x => x.GapDate.Year == year).Count);
            sw.WriteLine("Кол-во гэпов закрывшихся c откатом до 200 пунктов в {0} году: {1} шт", year, sortedList.FindAll(x => x.GapDate.Year == year && x.PullbackPoints < 200).Count);
            sw.WriteLine("Сумму гэпов с откатом до 200 пунктов в {0} году: {1}", year, Math.Round(sortedList.FindAll(x => x.GapDate.Year == year && x.PullbackPoints < 200).Sum(y=>y.GapWidth),2));

            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 50 пунктов в {0} году: {1} шт", year, less50));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 100 пунктов в {0} году: {1} шт", year, c50_100));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 200 пунктов в {0} году: {1} шт", year, c100_200));
            //sw.WriteLine(String.Format("Кол-во гэпов с откатом до 125 пунктов: {0} %", Math.Round((decimal)c100_125 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во гэпов с откатом до 150 пунктов: {0} %", Math.Round((decimal)c125_150 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во гэпов с откатом до 175 пунктов: {0} %", Math.Round((decimal)c150_175 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во гэпов с откатом до 200 пунктов: {0} %", Math.Round((decimal)c175_200 / close0WeekCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 300 пунктов в {0} году: {1} шт", year, c200_300));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 400 пунктов в {0} году: {1} шт", year, c300_400));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 500 пунктов в {0} году: {1} шт", year, c400_500));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 600 пунктов в {0} году: {1} шт", year, c500_600));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 700 пунктов в {0} году: {1} шт", year, c600_700));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 800 пунктов в {0} году: {1} шт", year, c700_800));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 900 пунктов в {0} году: {1} шт", year, c800_900));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом до 1000 пунктов в {0} году: {1} шт", year, c900_1000));
            sw.WriteLine(String.Format("Кол-во гэпов с откатом более 1000 пунктов в {0} году: {1} шт", year, cMore1000));
            sw.WriteLine("***************");
        }

        private void ConsecuteviLossAndProfit(List<GapFibo> sortedList, int sortPullback, out int maxLossCount, out int maxProfitCount, out double avgLossCount, out double avgProfitCount)
        {
            List<int> losses = new List<int>();
            List<int> profits = new List<int>();

            int curLossCount = 0;
            int curProfitCount = 0;

            for (int i = 0; i < sortedList.Count; i++)
            {
                if (sortedList[i].PullbackPoints > sortPullback || sortedList[i].GapDurationBeforeCloseInDays > oneWeek)
                {
                    profits.Add(curProfitCount);
                    curProfitCount = 0;
                    curLossCount++;
                }
                else
                {
                    losses.Add(curLossCount);
                    curLossCount = 0;
                    curProfitCount++;
                }

            }

            maxLossCount = losses.Max();
            avgLossCount = losses.Average();
            maxProfitCount = profits.Max();
            avgProfitCount = profits.Average();
        }

        

        private void AddGapAsClosed(Candle curCandle, GapFibo curGap, int curCandleNumber)
        {
            curGap.DayGapClosed = curCandle.Time.DayOfWeek;
            curGap.GapCloseDate = curCandle.Time;
            curGap.GapDurationBeforeCloseInDays = Math.Round((decimal)(curCandleNumber - curGap.GapCandleNumber)/720,1);

            int place = allGaps.FindIndex(x => x.GapCandleNumber == curGap.GapCandleNumber);
            allGaps.RemoveAt(place);
            allGaps.Insert(place, curGap);

            closedGapsToClear.Add(curGap);
        }

        private void ClearClosedGaps()
        {
            foreach (GapFibo g in closedGapsToClear)
                notClosedGaps.Remove(g);

            closedGapsToClear.Clear();
        }
    }
}
